﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Interactive.Utility.Extension
{
    /// <summary>
    /// 公共基类Object扩展方法
    /// </summary>
    public static class ObjectExt
    {
        #region 安全转换

        /// <summary>
        /// 扩展方法,得到Str
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static string AsStr(this object source, string defvalue)
        {
            if (source == null)
                return defvalue;
            return source.ToString();
        }

        /// <summary>
        /// 扩展方法,得到Str,如果为空,则返回&quot;&quot;
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string AsStr(this object source)
        {
            return source.AsStr("");
        }

        /// <summary>
        /// 扩展方法,得到Double
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static double AsDouble(this object source, double defvalue)
        {
            if (source == null)
                return defvalue;
            double tempDouble;
            if (double.TryParse(source.ToString(), out tempDouble))
                return tempDouble;
            return defvalue;
        }

        /// <summary>
        /// 扩展方法,得到Double
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static double AsDouble(this object source)
        {
            return AsDouble(source, 0.0);
        }

        /// <summary>
        /// 扩展方法,得到Int
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static int AsInt(this object source, int defvalue)
        {
            if (source == null)
                return defvalue;
            int tempInt;
            if (int.TryParse(source.ToString(), out tempInt))
                return tempInt;
            return defvalue;
        }

        /// <summary>
        /// 扩展方法,得到Int,如果为空,或者转换失败,则返回0
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static int AsInt(this object source)
        {
            return source.AsInt(0);
        }

        /// <summary>
        /// 扩展方法,得到Int
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static Int64 AsInt64(this object source, Int64 defvalue)
        {
            if (source == null)
                return defvalue;
            Int64 tempInt;
            if (Int64.TryParse(source.ToString(), out tempInt))
                return tempInt;
            return defvalue;
        }

        /// <summary>
        /// 扩展方法,得到Int,如果为空,或者转换失败,则返回0
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static Int64 AsInt64(this object source)
        {
            return source.AsInt64(0);
        }

        /// <summary>
        /// 扩展方法,得到Byte
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static byte AsByte(this object source, Byte defvalue)
        {
            if (source == null)
                return defvalue;
            Byte tempByte;
            if (byte.TryParse(source.ToString(), out tempByte))
                return tempByte;
            return defvalue;
        }

        /// <summary>
        /// 扩展方法,得到Byte,如果为空,或者转换失败,则返回0
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static byte AsByte(this object source)
        {
            return source.AsByte(0);
        }


        /// <summary>
        /// 扩展方法,得到Int
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static short AsShort(this object source, short defvalue)
        {
            if (source == null)
                return defvalue;
            short tempShort;
            if (short.TryParse(source.ToString(), out tempShort))
                return tempShort;
            return defvalue;
        }

        /// <summary>
        /// 扩展方法,得到Int,如果为空,或者转换失败,则返回0
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static short AsShort(this object source)
        {
            return source.AsShort(0);
        }


        /// <summary>
        /// 扩展方法,得到bool
        /// </summary>
        /// <param name="source"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static bool AsBool(this object source, bool defvalue)
        {
            if (source == null)
                return defvalue;
            bool tempBool;
            if (bool.TryParse(source.ToString(), out tempBool))
                return tempBool;
            return defvalue;
        }

        /// <summary>
        /// 扩展方法,得到Bool,如果为空,或者转换失败,则返回false
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool AsBool(this object source)
        {
            return source.AsBool(false);
        }


        /// <summary>
        /// datetime类型
        /// </summary>
        public static DateTime AsDatetime(this object source, DateTime defvalue)
        {
            if (source == null) return defvalue;
            DateTime tempDateTime;
            if (DateTime.TryParse(source.ToString(), out tempDateTime))
                return tempDateTime;
            return defvalue;
        }

        #endregion

        #region  非安全转换

        /// <summary>
        /// 非安排转换,但是在确认是正确的条件下,性能相对比较高
        /// 在reader[""]等地方可以使用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T AsTUnSafe<T>(this object source)
        {
            if (source == null)
                return default(T);
            return (T)source;
        }

        #endregion

        #region 判断

        /// <summary>
        /// 判断对象是否为Int32类型的数字
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static bool IsNumeric(this object expression)
        {
            if (expression != null)
            {
                string str = expression.ToString();
                if (str.Length > 0 && str.Length <= 11 && Regex.IsMatch(str, @"^[-]?[0-9]*[.]?[0-9]*$"))
                {
                    if ((str.Length < 10) || (str.Length == 10 && str[0] == '1') ||
                        (str.Length == 11 && str[0] == '-' && str[1] == '1'))
                        return true;
                }
            }
            return false;
        }


        /// <summary>
        /// 验证是否为正整数
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsInt(this object str)
        {
            if (str == null)
                return false;
            return Regex.IsMatch(str.ToString(), @"^[0-9]*$");
        }

        /// <summary>
        /// 是否为Double类型
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static bool IsDouble(this object expression)
        {
            if (expression != null)
                return Regex.IsMatch(expression.ToString(), @"^([0-9])[0-9]*(\.\w*)?$");
            return false;
        }


        /// <summary>
        /// 判断字符串是否是yy-mm-dd字符串
        /// </summary>
        /// <param name="str">待判断字符串</param>
        /// <returns>判断结果</returns>
        public static bool IsDateString(this object str)
        {
            if (str == null)
                return false;
            return Regex.IsMatch(str.ToString(), @"(\d{4})-(\d{1,2})-(\d{1,2})");
        }


        /// <summary>
        /// 判断字符是否是Null
        /// </summary>
        /// <param name="obj">待判断的字符</param>
        /// <returns></returns>
        public static bool IsNull(this object obj)
        {
            return obj == null;
        }

        #endregion

        #region ToString方法扩展

        private const string pattern3 = @"\[(?<Name>[^\[\]:]+)(\s*:\s*(?<Format>[^\[\]:]+))?\]";
        private const string pattern4 = @"\[(?<Name>[^\[\]:]+)(\s*[:]\s*(?<Format>[^\[\]:]+))?\]";
        private const string pattern5 = @"\[(?<Name>[^\[\]:]+)(\s*[:]\s*(?<Format>[^\[\]:]+))?\]";


        /// <summary>
        /// 可以支持如下形式:People:Id[Id:d4],Name [Name],Birthday [Birthday:yyyy-MM-dd]
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static string ToString3(this object obj, string format)
        {
            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties(
                BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance);

            MatchEvaluator evaluator = match =>
            {
                string propertyName = match.Groups["Name"].Value;
                string propertyFormat = match.Groups["Format"].Value;

                PropertyInfo propertyInfo =
                    properties.FirstOrDefault(p => p.Name == propertyName);
                if (propertyInfo != null)
                {
                    object propertyValue = propertyInfo.GetValue(obj, null);
                    if (string.IsNullOrEmpty(propertyFormat) == false)
                        return string.Format("{0:" + propertyFormat + "}", propertyValue);
                    return propertyValue.ToString();
                }
                return match.Value;
            };

            return Regex.Replace(format, pattern3, evaluator, RegexOptions.Compiled);
        }


        public static string ToString4(this object obj, string format)
        {
            MatchEvaluator evaluator = match =>
            {
                string[] propertyNames = match.Groups["Name"].Value.Split('.');
                string propertyFormat = match.Groups["Format"].Value;

                object propertyValue = obj;
                try
                {
                    foreach (string propertyName in propertyNames)
                        propertyValue = propertyValue.GetPropertyValue(propertyName);
                }
                catch
                {
                    return match.Value;
                }

                if (string.IsNullOrEmpty(format) == false)
                    return string.Format("{0:" + propertyFormat + "}", propertyValue);
                return propertyValue.ToString();
            };

            return Regex.Replace(format, pattern4, evaluator, RegexOptions.Compiled);
        }

        public static object GetPropertyValue(this object obj, string propertyName)
        {
            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(propertyName);
            return info.GetValue(obj, null);
        }


        public static string ToString5(this object obj, string format)
        {
            MatchEvaluator evaluator = match =>
            {
                string[] propertyNames = match.Groups["Name"].Value.Split('.');
                string propertyFormat = match.Groups["Format"].Value;

                object propertyValue = obj;

                try
                {
                    foreach (string propertyName in propertyNames)
                        propertyValue = propertyValue.GetPropertyValue(propertyName);
                }
                catch
                {
                    return match.Value;
                }

                if (string.IsNullOrEmpty(propertyFormat) == false)
                {
                    if (propertyFormat.StartsWith("."))
                    {
                        string subPropertyName = propertyFormat.Substring(1);
                        IEnumerable<object> objs =
                            ((IEnumerable)propertyValue).Cast<object>();
                        if (subPropertyName == "Count")
                            return objs.Count().ToString();
                        string[] subProperties = objs.Select(
                            o => o.GetPropertyValue(subPropertyName).ToString()).ToArray();
                        return string.Join(", ", subProperties);
                    }
                    return string.Format("{0:" + propertyFormat + "}", propertyValue);
                }
                return propertyValue.ToString();
            };
            return Regex.Replace(format, pattern5, evaluator, RegexOptions.Compiled);
        }

        #endregion

        #region DateTime的相关拓展
        private static readonly DateTime BeginTime = new DateTime(2010, 1, 1);
        private const long Ticks = 10000000L;

        /// <summary>
        /// 得到一个时间的整数表示
        /// </summary>
        /// <param name="dateTime">时间要在1941~2078</param>
        /// <returns></returns>
        public static int AsInt(this DateTime dateTime)
        {
            var ticks = dateTime.Ticks - BeginTime.Ticks;
            ticks /= Ticks;
            return (int)ticks;
        }

        /// <summary>
        /// 通过整型得到时间
        /// </summary>
        /// <param name="time">整形时间</param>
        /// <returns></returns>
        public static DateTime AsDatetime(this int time)
        {
            return new DateTime(time * Ticks + BeginTime.Ticks);
        }
        #endregion

    }
}